package p2pcdnsim.cdn;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Element;


import p2pcdnsim.exception.SurrogateAlreadyInsertedException;
import p2pcdnsim.network.Link;
import p2pcdnsim.network.RouterNode;
import p2pcdnsim.xml.XMLHandler;

public class HotSpotPlacement implements PlacementStrategy{

	private XMLHandler requests;
	@SuppressWarnings("unused")
	private boolean sizeRequests;
	private int raio;
	private Map<Long,Load> loads;
	private Map<Long,Load>loads2;
	
	public HotSpotPlacement(boolean sizeRequests, XMLHandler requests) {
		this.sizeRequests = sizeRequests;
		this.requests = requests;
		this.raio = 0;
	}
	
	public HotSpotPlacement(boolean sizeRequests, XMLHandler requests, int raio) {
		this.sizeRequests = sizeRequests;
		this.requests = requests;
		this.raio = raio;
	}
	
	@Override
	public void placeSurrogates(List<Surrogate> surrogates, CDN cdn)
			throws SurrogateAlreadyInsertedException {
		
		RouterNode[] nodes = cdn.getTopology().getNodes();
		
		// Define the loads
		this.loads = new HashMap<Long,Load>();
		{
			for(RouterNode node: nodes) this.loads.put(node.getNodeId(), new Load(node));
			Iterator<Element> i = this.requests.iterator();
			while (i.hasNext()) {
				Element linksTemp = i.next();
				//int objectID  = Integer.parseInt(linksTemp.getChild("VideoObject").getText());
				long nodeId = cdn.getNodeOfClinet(Long.parseLong(linksTemp.getChild("Client").getText()));
				Load load = this.loads.get(nodeId);
				load.addLoad(1);
				//System.out.println("Load on router "+nodeId+": "+load);
			}
			
		}
		
		this.loads2 = new HashMap<Long,Load>();
		{
			long temp = 0;
			Object[] loadsKey = this.loads.keySet().toArray();
			
			for(RouterNode node: nodes) this.loads2.put(node.getNodeId(), new Load(node));
			
			for (int i = 0; i < loadsKey.length; i++) {
				temp = neighborhoodLoad(this.loads.get(loadsKey[i]), this.raio);
				this.loads2.get(loadsKey[i]).load = this.loads.get(loadsKey[i]).load + temp;
			}
			
		}
		
		//Put the loads on a list to sort
		List<Load> listLoads = new ArrayList<Load>();
		listLoads.addAll(this.loads2.values());
		Collections.sort(listLoads);

		int cont = listLoads.size() - 1;
		
		for (Surrogate surrogate : surrogates) {
			cdn.placeSurrogate(surrogate, listLoads.get(cont).node);
			cont--;
		}
	
	}
	
	private long neighborhoodLoad(Load load, int r) {
		long retorno = 0;
		long temp = 0;
		
		List<Link> links = load.getRouterNode().getLinks();
		
		if(r == 0){
		
			retorno = 0;
		
		}else {
			
			for (Iterator<Link> iterator = links.iterator(); iterator.hasNext();) {
				Link link = iterator.next();
				temp = neighborhoodLoad(this.loads.get(link.getNodeTo().getNodeId()), r - 1 );
				retorno = retorno + this.loads.get(link.getNodeTo().getNodeId()).load + temp;
			}
			
		}
		
		
		return retorno;
	}

	private class Load implements Comparable<Load> {
		RouterNode node;
		long load;
		public Load(RouterNode node) {
			this.node = node;
			load = 0;
		}
		public void addLoad(long l) {
			load+=l;
		}
		
		@Override
		public int compareTo(Load o) {
			return new Long(load).compareTo(new Long(o.load));
		}
		
		public RouterNode getRouterNode(){
			return node;
		}
	}
	
//	public RouterNode[] nodes;
//	public HashMap<RouterNode, Integer> requests;
//	public ClientPlacement clientPlacement;
//	
//	@Override
//	public void placeSurrogates(List<Surrogate> surrogates, CDN cdn)
//			throws SurrogateAlreadyInsertedException {
//		
//		this.requests = new HashMap<RouterNode,Integer>();
//		this.nodes = cdn.getTopology().getNodes();
//		
//		String file = "xml/Requests.xml";
//		String file2 = "xml/ClientPlacement.xml";
//		SAXBuilder builder = new SAXBuilder();
//		SAXBuilder builder2 = new SAXBuilder();
//		Document requests = new Document();
//		Document placement = new Document();
//		
//		try {
//			requests = builder.build(new File(file));
//			placement = builder2.build(new File(file2));
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//		
//		this.clientPlacement = new ClientPlacement(requests,placement, this.nodes);
//		this.requests = clientPlacement.getRoutersRequests();
//		//this.nodes = ordena();
//		randomizedSelect(0, this.nodes.length-1, this.nodes.length - surrogates.size());
//		int cont = this.nodes.length - 1;
//		
//		for (Surrogate surrogate : surrogates) {
//			cdn.placeSurrogate(surrogate, nodes[cont]);
//			cont--;
//		}
//	}
//	
//	private int randomizedSelect(int begin, int end, int indice){
//		int q, k;
//		
//		if(begin == end){
//			return begin;
//		}
//		
//		q = randomizedPartition(begin,end);
//		k = q - begin - 1;
//		
//		if(indice == k){
//			return q;
//		}else if(indice < k){
//			return randomizedSelect(begin, q - 1, indice);
//		}else {
//			return randomizedSelect(q + 1, end, indice - k);
//		}
//	}
//
//	private int randomizedPartition(int begin, int end) {
//		int i = (((int)Math.random())%(end - begin + 1) + begin);
//		
//		RouterNode temp = this.nodes[i];
//		this.nodes[i] = this.nodes[begin];
//		this.nodes[begin] = temp;
//		
//		return partition(begin,end);
//	}
//
//	private int partition(int begin, int end) {
//		int i;
//		RouterNode x = this.nodes[end];
//		i = begin - 1;
//		
//		for (int j = begin; j < end; j++) {
//			
//			if(this.requests.get(this.nodes[j]) <= this.requests.get(x)){
//				i = i + 1;
//				RouterNode temp;
//				temp = this.nodes[i];
//				this.nodes[i] = this.nodes[j];
//				this.nodes[j] = temp;
//			}
//		
//		}
//		
//		RouterNode temp;
//		temp = this.nodes[i+1];
//		this.nodes[i+1] = this.nodes[end];
//		this.nodes[end] = temp;
//		
//		return (i+1);
//	}
	
		
}
